{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "本文适应人群：`C#` or `Python3` 基础巩固\n",
    "\n",
    "代码裤子： <a href=\"https://github.com/lotapp/BaseCode\" target=\"_blank\">https://github.com/lotapp/BaseCode</a>\n",
    "\n",
    "在线编程： <a href=\"https://mybinder.org/v2/gh/lotapp/BaseCode/master\" target=\"_blank\">https://mybinder.org/v2/gh/lotapp/BaseCode/master</a>\n",
    "\n",
    "在线预览：<a href=\"http://github.lesschina.com/python/base/ext/基础衍生.html\" target=\"_blank\">http://github.lesschina.com/python/base/ext/基础衍生.html</a>\n",
    "\n",
    "马上快期末考试了，老师蜜月也回来了，于是有了一场跨季度的复习讲课了：\n",
    "\n",
    "## 1.Python基础语法扩展\n",
    "\n",
    "### 1.1.if 判断条件相关\n",
    "\n",
    "`None、\"\"、0、[]、{}` ==> 假\n",
    "\n",
    "`1、\" \"、[None,\"\"]、{\"\":None}` ==> 真\n",
    "\n",
    "小明可高兴了，前几天被打击的面目全非，这几天老师回来了，又可以大发神威了，于是抢先提交demo："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "False\n"
     ]
    }
   ],
   "source": [
    "# None\n",
    "if None:\n",
    "    print(True)\n",
    "else:\n",
    "    print(False)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "False\n"
     ]
    }
   ],
   "source": [
    "# 0为False\n",
    "if 0:\n",
    "    print(True)\n",
    "else:\n",
    "    print(False)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "False\n"
     ]
    }
   ],
   "source": [
    "# 空字符串\n",
    "if \"\":\n",
    "    print(True)\n",
    "else:\n",
    "    print(False)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "False\n"
     ]
    }
   ],
   "source": [
    "# 空列表为False\n",
    "if []:\n",
    "    print(True)\n",
    "else:\n",
    "    print(False)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "False\n"
     ]
    }
   ],
   "source": [
    "# 空字典为False\n",
    "if {}:\n",
    "    print(True)\n",
    "else:\n",
    "    print(False)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "True\n"
     ]
    }
   ],
   "source": [
    "# 1为True\n",
    "if 1:\n",
    "    print(True)\n",
    "else:\n",
    "    print(False)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "True\n"
     ]
    }
   ],
   "source": [
    "# 含空格\n",
    "if \" \":\n",
    "    print(True)\n",
    "else:\n",
    "    print(False)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "True\n"
     ]
    }
   ],
   "source": [
    "if [None,\"\"]:\n",
    "    print(True)\n",
    "else:\n",
    "    print(False)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "True\n"
     ]
    }
   ],
   "source": [
    "if {\"\":None}:\n",
    "    print(True)\n",
    "else:\n",
    "    print(False)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "老师微带笑容的看了小明一眼，然后接着讲if的扩展\n",
    "\n",
    "### 1.2.三元表达符\n",
    "\n",
    "eg:`max = a if a > b else b`"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "2\n"
     ]
    }
   ],
   "source": [
    "a, b = 1, 2\n",
    "\n",
    "max = a if a > b else b\n",
    "\n",
    "print(max)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "3\n"
     ]
    }
   ],
   "source": [
    "a, b, c = 1, 3, 2\n",
    "\n",
    "max = a if a > b else b\n",
    "max = max if max > c else c\n",
    "\n",
    "print(max)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "3\n"
     ]
    }
   ],
   "source": [
    "# 上面的那个还有一种简写（不推荐）\n",
    "a, b, c = 1, 3, 2\n",
    "\n",
    "max = (a if a > b else b) if (a if a > b else b) > c else c\n",
    "\n",
    "print(max)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 1.2.字符串和编码\n",
    "\n",
    "在`Python3.x`版本中，字符串是以`Unicode`编码的\n",
    "\n",
    "对于单个字符的编码，Python提供了`ord()`函数获取字符的整数表示，`chr()`函数把编码转换为对应的字符\n",
    "\n",
    "小潘对这块有所研究，把小明按在桌上然后抢先提交demo："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "68"
      ]
     },
     "execution_count": 13,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "ord('D')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "27602"
      ]
     },
     "execution_count": 14,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "ord('毒')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'D'"
      ]
     },
     "execution_count": 15,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "chr(68)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'毒'"
      ]
     },
     "execution_count": 16,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "chr(27602)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "65\n",
      "90\n",
      "97\n",
      "122\n"
     ]
    }
   ],
   "source": [
    "print(ord('A'))\n",
    "print(ord('Z'))\n",
    "\n",
    "print(ord('a'))\n",
    "print(ord('z'))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "老师补充讲解道：\n",
    "\n",
    "编码：`encode()` 解码：`decode()`\n",
    "\n",
    "url相关的可以用：\n",
    "\n",
    "`urllib.parse.quote()` and `urllib.parse.unquote()`\n",
    "\n",
    "\n",
    "`urllib.parse.urlencode()` 可以直接对一个`key-value`进行`url`编码"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "b'\\xe6\\xaf\\x92\\xe9\\x80\\x86\\xe5\\xa4\\xa9'\n",
      "毒逆天\n"
     ]
    }
   ],
   "source": [
    "# encode() and decode()\n",
    "name=\"毒逆天\"\n",
    "\n",
    "name_encode=name.encode(\"utf-8\")\n",
    "\n",
    "print(name_encode)\n",
    "\n",
    "print(name_encode.decode(\"utf-8\"))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 需要导入urlib.parse\n",
    "\n",
    "import urllib.parse"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "%E6%B7%A1%E5%AE%9A\n",
      "淡定\n"
     ]
    }
   ],
   "source": [
    "test_str=\"淡定\"\n",
    "\n",
    "# 对字符串进行url编码和解码\n",
    "test_str_enode = urllib.parse.quote(test_str)\n",
    "\n",
    "print(test_str_enode)\n",
    "\n",
    "# urllib.parse.quote() 解码\n",
    "print(urllib.parse.unquote(test_str_enode))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "metadata": {
    "scrolled": true
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "name=%E6%AF%92%E9%80%86%E5%A4%A9&age=23\n",
      "name=毒逆天&age=23\n"
     ]
    }
   ],
   "source": [
    "# urlencode 可以直接对一个key-value进行编码\n",
    "\n",
    "test_dict={\"name\":\"毒逆天\",\"age\":23}\n",
    "\n",
    "encode_str = urllib.parse.urlencode(test_dict)\n",
    "\n",
    "print(encode_str)\n",
    "print(urllib.parse.unquote(encode_str))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "### 1.3.值判断和地址判断\n",
    "\n",
    "小明不乐意了，你个小潘总是抢我的风头，看完标题就刷刷的在黑板上写下了如下知识点：\n",
    "\n",
    "**is 是比较两个引用是否指向了同一个对象**（`id()`得到的地址一样则相同）\n",
    "\n",
    "**== 是比较两个对象的值是否相等**\n",
    "\n",
    "在之前讲Dict的时候提了一下可变和不可变类型：<a href=\"https://www.cnblogs.com/dotnetcrazy/p/9155310.html#5.2.增加和修改\" target=\"_blank\">https://www.cnblogs.com/dotnetcrazy/p/9155310.html#5.2.增加和修改</a>\n",
    "\n",
    "Func里面又系统的说了一下：<a href=\"https://www.cnblogs.com/dotnetcrazy/p/9175950.html#4.5.可变类型和不可变类型\" target=\"_blank\">https://www.cnblogs.com/dotnetcrazy/p/9175950.html#4.5.可变类型和不可变类型</a>\n",
    "\n",
    "对于可变不可变系列就不去复述了，下面再来几个案例看看 **值判断**和 **地址判断**的概念"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "metadata": {},
   "outputs": [],
   "source": [
    "################ 可变类型 ################ "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "139727165899464\n",
      "139727165725256\n"
     ]
    }
   ],
   "source": [
    "a=[1,2,3]\n",
    "b=[1,2,3]\n",
    "\n",
    "# id不一样，那is肯定不一样了\n",
    "print(id(a))\n",
    "print(id(b))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "False"
      ]
     },
     "execution_count": 24,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# a和b是否指向同一个地址\n",
    "a is b"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 25,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "True"
      ]
     },
     "execution_count": 25,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# a和b的值是否相同\n",
    "a == b"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 26,
   "metadata": {},
   "outputs": [],
   "source": [
    "################ 开始变化了 ################ "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 27,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "139727165725256\n",
      "139727165725256\n"
     ]
    }
   ],
   "source": [
    "# 让a指向b的地址\n",
    "a=b\n",
    "\n",
    "# a和b的id一样了\n",
    "print(id(a))\n",
    "print(id(b))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 28,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "True"
      ]
     },
     "execution_count": 28,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# a和b是否指向同一个地址\n",
    "a is b"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 29,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "True"
      ]
     },
     "execution_count": 29,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# a和b的值是否相同\n",
    "a == b"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 30,
   "metadata": {},
   "outputs": [],
   "source": [
    "################ 不可变类型 ################ "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 31,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "94592578394656\n",
      "94592578394656\n"
     ]
    }
   ],
   "source": [
    "a=1\n",
    "b=1\n",
    "\n",
    "# id一样\n",
    "print(id(a))\n",
    "print(id(b))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 32,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "True"
      ]
     },
     "execution_count": 32,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "a is b"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 33,
   "metadata": {
    "scrolled": true
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "True"
      ]
     },
     "execution_count": 33,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "a == b"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 34,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "139727217917024\n",
      "139727217917096\n"
     ]
    }
   ],
   "source": [
    "# 但是你要注意，不是所有不可变类型都这样的\n",
    "\n",
    "f1=1.2\n",
    "f2=1.2\n",
    "\n",
    "# 声明两个相同值的浮点型变量，查看它们的id，发现它们并不是指向同个内存地址(这点和int类型不同)\n",
    "print(id(f1))\n",
    "print(id(f2))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 35,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "False"
      ]
     },
     "execution_count": 35,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 这个就不一样了\n",
    "# 这方面涉及Python内存管理机制，Python对int类型和较短的字符串进行了缓存\n",
    "# 无论声明多少个值相同的变量，实际上都指向同个内存地址，其他的就没这福利咯～\n",
    "\n",
    "f1 is f2"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 36,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "True"
      ]
     },
     "execution_count": 36,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "f1 == f2"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 2.Python总结之for系列\n",
    "\n",
    "老师徐徐道来：“之前说for总是零零散散的，现在基础都讲完了，来个小汇总：”\n",
    "\n",
    "### 2.1.Base\n",
    "\n",
    "**能够被for循环遍历的，就是可迭代的**\n",
    "\n",
    "For基础系：<a href=\"https://www.cnblogs.com/dotnetcrazy/p/9102030.html#9.1.Python\" target=\"_blank\">https://www.cnblogs.com/dotnetcrazy/p/9102030.html#9.1.Python</a>"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 37,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "0\n",
      "1\n",
      "2\n",
      "3\n",
      "4\n"
     ]
    }
   ],
   "source": [
    "# 类似于for(int i=0;i<5;i++)\n",
    "\n",
    "for i in range(5):\n",
    "    print(i)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 38,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "https://pan.baidu.com/s/1weaF2DGsgDzAcniRzNqfyQ\n",
      " end ...\n"
     ]
    }
   ],
   "source": [
    "#while循环一般通过数值是否满足来确定循环的条件\n",
    "#for循环一般是对能保存多个数据的变量，进行遍历\n",
    "\n",
    "name=\"https://pan.baidu.com/s/1weaF2DGsgDzAcniRzNqfyQ#mmd\"\n",
    "\n",
    "for i in name:\n",
    "    if i=='#':\n",
    "        break\n",
    "    print(i,end='')#另一种写法：print(\"%s\"%i,end=\"\")\n",
    "print('\\n end ...')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 39,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "i = 5\n",
      "没有找到\n"
     ]
    }
   ],
   "source": [
    "# 你期望的结果是：i = 5\n",
    "\n",
    "for i in range(10):\n",
    "    if i == 5:\n",
    "        print(\"i = %d\" % i)\n",
    "else:\n",
    "    print(\"没有找到\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 40,
   "metadata": {
    "scrolled": true
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "i = 5\n"
     ]
    }
   ],
   "source": [
    "# 当迭代的对象迭代完并为空时，位于else的子句将执行\n",
    "# 而如果在for循环中含有break时则直接终止循环，并不会执行else子句\n",
    "# 正确写法如下：\n",
    "\n",
    "for i in range(10):\n",
    "    if i == 5:\n",
    "        print(\"i = %d\" % i)\n",
    "        break\n",
    "else:\n",
    "    print(\"没有找到\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 41,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "key:Name,value:小明\n",
      "key:Age,value:23\n"
     ]
    }
   ],
   "source": [
    "# 遍历一个字典\n",
    "\n",
    "test_dict={\"Name\":\"小明\",\"Age\":23}\n",
    "\n",
    "for k,v in test_dict.items():\n",
    "    print(\"key:%s,value:%s\"%(k,v))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 2.2.列表生成式\n",
    "\n",
    "如果下面知识点还不熟悉的，看看之前讲的～列表生成式：<a href=\"https://www.cnblogs.com/dotnetcrazy/p/9155310.html#1.9.列表生成式\" target=\"_blank\">https://www.cnblogs.com/dotnetcrazy/p/9155310.html#1.9.列表生成式</a>\n",
    "\n",
    "**简写**：`list(range(1, 11))`\n",
    "**全写**：`[x for x in range(1,11)]`"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 42,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]"
      ]
     },
     "execution_count": 42,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "list(range(1, 11))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 43,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]"
      ]
     },
     "execution_count": 43,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "[x for x in range(1,11)]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 44,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]"
      ]
     },
     "execution_count": 44,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 1~10的平方列表\n",
    "[x*x for x in range(1,11)]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 45,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[2, 4, 6, 8, 10]"
      ]
     },
     "execution_count": 45,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 1~10之间的偶数\n",
    "[x for x in range(1, 11) if x % 2 == 0]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 46,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "['AA', 'AB', 'BA', 'BB', 'CA', 'CB']"
      ]
     },
     "execution_count": 46,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 数学里面的全排列\n",
    "[x + y for x in 'ABC' for y in 'AB']"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 47,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[(1, 1),\n",
       " (1, 2),\n",
       " (1, 3),\n",
       " (2, 1),\n",
       " (2, 2),\n",
       " (2, 3),\n",
       " (3, 1),\n",
       " (3, 2),\n",
       " (3, 3),\n",
       " (4, 1),\n",
       " (4, 2),\n",
       " (4, 3)]"
      ]
     },
     "execution_count": 47,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 数学里面的坐标轴\n",
    "[(x,y) for x in range(1,5) for y in range(1,4)]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 48,
   "metadata": {
    "scrolled": true
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[(1, 1, 1),\n",
       " (1, 1, 2),\n",
       " (1, 2, 1),\n",
       " (1, 2, 2),\n",
       " (1, 3, 1),\n",
       " (1, 3, 2),\n",
       " (2, 1, 1),\n",
       " (2, 1, 2),\n",
       " (2, 2, 1),\n",
       " (2, 2, 2),\n",
       " (2, 3, 1),\n",
       " (2, 3, 2),\n",
       " (3, 1, 1),\n",
       " (3, 1, 2),\n",
       " (3, 2, 1),\n",
       " (3, 2, 2),\n",
       " (3, 3, 1),\n",
       " (3, 3, 2),\n",
       " (4, 1, 1),\n",
       " (4, 1, 2),\n",
       " (4, 2, 1),\n",
       " (4, 2, 2),\n",
       " (4, 3, 1),\n",
       " (4, 3, 2)]"
      ]
     },
     "execution_count": 48,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# (x,y,z) 一般三个嵌套就上天了\n",
    "[(x,y,z) for x in range(1,5) for y in range(1,4) for z in range(1,3)]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 2.3.扩展\n",
    "\n",
    "**如果要对list实现类似C#或者java那样的下标循环怎么办？**\n",
    "\n",
    "这块小明又有预习，于是在提交Code的同时大声说道：\n",
    "\n",
    "Python内置的`enumerate`函数可以把一个list变成`索引-元素`对，这样就可以在for循环中同时迭代`索引和元素本身`"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 49,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "0 A\n",
      "1 B\n",
      "2 C\n"
     ]
    }
   ],
   "source": [
    "for i, item in enumerate(['A', 'B', 'C']):\n",
    "    print(i, item)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 3.Python中赋值、浅拷贝、深拷贝\n",
    "\n",
    "看到标题小明和小潘就楞了，老师当时没讲解啊，然后两个人眼巴巴的看着老师讲解：\n",
    "\n",
    "官方文档：<a href=\"https://docs.python.org/3/library/copy.html\" target=\"_blank\">https://docs.python.org/3/library/copy.html</a>\n",
    "\n",
    "### 3.1.赋值\n",
    "\n",
    "通过`=`来实现，就是把地址拷贝了一份，比如 `a = b`"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 50,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "139727165518536\n",
      "139727165518536\n"
     ]
    }
   ],
   "source": [
    "a=[1,2,2]\n",
    "b = a\n",
    "\n",
    "print(id(a))\n",
    "print(id(b))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 51,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[1, 2, 2, 3]\n",
      "[1, 2, 2, 3]\n"
     ]
    }
   ],
   "source": [
    "# 再验证\n",
    "\n",
    "a.append(3)\n",
    "\n",
    "# 都增加了一个3，说明的确指向同一个内存地址\n",
    "print(a)\n",
    "print(b)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 3.2.深拷贝deepcopy\n",
    "\n",
    "导入`copy`模块，调用`deepcopy`方法\n",
    "\n",
    "如果有嵌套引用的情况，直接递归拷贝"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 52,
   "metadata": {},
   "outputs": [],
   "source": [
    "import copy\n",
    "\n",
    "a=[1,2,2]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 53,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "139727165899080\n",
      "139727165900488\n"
     ]
    }
   ],
   "source": [
    "b=copy.deepcopy(a)\n",
    "\n",
    "# 指向了不同的内存地址\n",
    "print(id(a))\n",
    "print(id(b))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 54,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[1, 2, 2, 3]\n",
      "[1, 2, 2]\n"
     ]
    }
   ],
   "source": [
    "# 再验证一下\n",
    "\n",
    "a.append(3)\n",
    "\n",
    "# b不变，说明的确指向不同的内存地址\n",
    "print(a)\n",
    "print(b)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 55,
   "metadata": {},
   "outputs": [],
   "source": [
    "################ 开始变化了 ################ "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 56,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "139727166586248\n",
      "139727165899080\n",
      "139727165725256\n",
      "139727165899464\n"
     ]
    }
   ],
   "source": [
    "# 之前讲了嵌套列表，我们来验证一下\n",
    "\n",
    "a=[1,2,2]\n",
    "b=[1,2,3,a]\n",
    "\n",
    "c=copy.deepcopy(b)\n",
    "\n",
    "# 发现地址都不一样\n",
    "print(id(b))\n",
    "print(id(c))\n",
    "print(id(b[3]))\n",
    "print(id(c[3]))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 57,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[1, 2, 3, [1, 2, 2, 666]]\n",
      "[1, 2, 3, [1, 2, 2]]\n"
     ]
    }
   ],
   "source": [
    "# 直观的验证一下\n",
    "\n",
    "a.append(666)\n",
    "\n",
    "# 深拷贝的确是深拷贝\n",
    "print(b)\n",
    "print(c)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 3.3.浅拷贝copy\n",
    "\n",
    "copy只是简单拷贝，如果拷贝内容里面还有引用之类的，他是不管的"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 58,
   "metadata": {},
   "outputs": [],
   "source": [
    "import copy\n",
    "\n",
    "a=[1,2,2]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 59,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "139727165902088\n",
      "139727165850952\n"
     ]
    }
   ],
   "source": [
    "b=copy.copy(a)\n",
    "\n",
    "# 指向了不同的内存地址\n",
    "print(id(a))\n",
    "print(id(b))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 60,
   "metadata": {},
   "outputs": [],
   "source": [
    "################ 开始变化了 ################ "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 61,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "139727165519432\n",
      "139727165902088\n"
     ]
    }
   ],
   "source": [
    "# 之前讲了嵌套列表，我们来验证一下\n",
    "\n",
    "a=[1,2,2]\n",
    "b=[1,2,3,a]\n",
    "\n",
    "c=copy.copy(b)\n",
    "\n",
    "# 第一层地址不一样\n",
    "print(id(b))\n",
    "print(id(c))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 62,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[1, 2, 3, [1, 2, 2], 111]\n",
      "[1, 2, 3, [1, 2, 2]]\n"
     ]
    }
   ],
   "source": [
    "# 验证一下\n",
    "b.append(111)\n",
    "\n",
    "# 第一层指向的不同地址\n",
    "print(b)\n",
    "print(c)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 63,
   "metadata": {
    "scrolled": true
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "139727165725576\n",
      "139727165725576\n"
     ]
    }
   ],
   "source": [
    "# 如果里面还有引用，那么就不管了\n",
    "print(id(b[3]))\n",
    "print(id(c[3]))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 64,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[1, 2, 3, [1, 2, 2, 666], 111]\n",
      "[1, 2, 3, [1, 2, 2, 666]]\n"
     ]
    }
   ],
   "source": [
    "# 验证一下\n",
    "a.append(666)\n",
    "\n",
    "# 内部引用的确没copy新地址\n",
    "print(b)\n",
    "print(c)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 3.4.知识扩展\n",
    "\n",
    "**如果拷贝的对象是不可变类型，不管深拷贝和浅拷贝以及赋值都是地址引用。`但当拷贝的不可变对象含有引用类型时，只有深拷贝(deepcopy)会递归复制`**\n",
    "\n",
    "需要注意的是：Python和Net对于值类型处理是不一样的（管理方式不一样导致的）\n",
    "\n",
    "==>`NET中值类型默认是深拷贝的，而对于引用类型，默认实现的是浅拷贝`"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 65,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "139727165526520\n",
      "139727165526520\n"
     ]
    }
   ],
   "source": [
    "a=(1,2,2)\n",
    "b=a\n",
    "\n",
    "print(id(a))\n",
    "print(id(b))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 66,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "139727165846872\n",
      "139727165846872\n"
     ]
    }
   ],
   "source": [
    "a=(1,2,2)\n",
    "b=copy.deepcopy(a)\n",
    "\n",
    "print(id(a))\n",
    "print(id(b))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 67,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "139727165526520\n",
      "139727165526520\n"
     ]
    }
   ],
   "source": [
    "a=(1,2,2)\n",
    "b=copy.copy(a)\n",
    "\n",
    "print(id(a))\n",
    "print(id(b))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**扩：当拷贝的不可变对象含有引用类型时：赋值和浅拷贝不会copy，而深拷贝(deepcopy)会递归复制**\n",
    "![深拷贝.png](https://img2018.cnblogs.com/blog/658978/201903/658978-20190314113927119-1660617447.png)\n",
    "\n",
    "**PS：我们常用的切片相当于浅拷贝**（`copy.copy()`）\n",
    "![切片.png](https://img2018.cnblogs.com/blog/658978/201903/658978-20190314121336517-2122258672.png)\n",
    "\n",
    "\n",
    "\n",
    "## 4.CSharp中赋值、浅拷贝、深拷贝\n",
    "\n",
    "小明听懂了Python的深拷贝和浅拷贝后，本着学以致用的原则，写下了C#的实现：\n",
    "\n",
    "先声明一下，本机环境是Ubuntu + NetCore，欢迎贴Code补充\n",
    "\n",
    "### 4.1.赋值\n",
    "\n",
    "Code：<a href=\"https://github.com/lotapp/BaseCode/tree/master/netcore/3_Ext/deepcopy\" target=\"_blank\">https://github.com/lotapp/BaseCode/tree/master/netcore/3_Ext/deepcopy</a>\n",
    "\n",
    "赋值方法和`Python`一样，直接赋值即可\n",
    "```csharp\n",
    "var list1 = new List<int>() { 1, 2, 2 };\n",
    "var list2 = list1;\n",
    "```"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 68,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1 2 2 3 \n",
      "1 2 2 3 "
     ]
    }
   ],
   "source": [
    "%%script csharp\n",
    "\n",
    "// Python一样，直接赋值即可\n",
    "var list1 = new List<int>() { 1, 2, 2 };\n",
    "var list2 = list1;\n",
    "\n",
    "// 验证一下\n",
    "list1.Add(3);//我们修改一下list1，list2也就跟着就改变了\n",
    "\n",
    "foreach (var item in list1)\n",
    "{\n",
    "    Console.Write(item + \" \");\n",
    "}\n",
    "Console.WriteLine();\n",
    "foreach (var item in list2)\n",
    "{\n",
    "    Console.Write(item + \" \");\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 4.2值类型默认深拷贝\n",
    "\n",
    "NetCore深拷贝相关的<a href=\"https://docs.microsoft.com/zh-cn/dotnet/api/system.collections.generic.list-1.copyto?view=netcore-2.1\" target=\"_blank\">官方文档</a> `public void CopyTo (T[] array);`\n",
    "\n",
    "\n",
    "简单类型用**最简单的方式**就能实现深拷贝了：\n",
    "\n",
    "官方的CopyTo在这里和这个效果一样，但是比较麻烦，这边就不贴了（Code里面贴了）\n",
    "\n",
    "```csharp\n",
    "var list3 = new List<int>() { 1, 2, 2 };\n",
    "var list4 = new List<int>(list3);\n",
    "\n",
    "// 验证一下\n",
    "list3.Add(3);\n",
    "foreach (var item in list3)\n",
    "{\n",
    "    Console.Write(item + \" \");\n",
    "}\n",
    "Console.WriteLine();\n",
    "foreach (var item in list4)\n",
    "{\n",
    "    Console.Write(item + \" \");\n",
    "}\n",
    "```\n",
    "结果：\n",
    "```\n",
    "1 2 2 3 \n",
    "1 2 2 \n",
    "```\n",
    "\n",
    "### 4.3.引用类型默认浅拷贝\n",
    "\n",
    "**对于`List<T>`再复杂点的，上面的方式就变成浅拷贝了：(类似于Python的Copy.Copy)**\n",
    "\n",
    "官方的CopyTo在这里和这个效果一样，但是比较麻烦，这边就不贴了（Demo里面贴了）\n",
    "\n",
    "定义一个Student\n",
    "```csharp\n",
    "public partial class Student\n",
    "{\n",
    "    public string Name { get; set; }\n",
    "    public int Age { get; set; }\n",
    "\n",
    "    public override string ToString()\n",
    "    {\n",
    "        return $\"Name:{Name},Age:{Age}\";\n",
    "    }\n",
    "}\n",
    "```\n",
    "\n",
    "---\n",
    "\n",
    "浅拷贝Demo：\n",
    "```csharp\n",
    "var list5 = new List<Student>(){\n",
    "    new Student { Name = \"小张\", Age = 22 },\n",
    "    new Student { Name = \"小明\", Age = 23 }\n",
    "    };\n",
    "var p = new Student() { Name = \"小潘\", Age = 23 };\n",
    "list5.Add(p);\n",
    "\n",
    "// 浅拷贝一份\n",
    "var list6 = new List<Student>(list5);\n",
    "\n",
    "// 浅拷贝测试\n",
    "// 我们修改一下list5，list6没有跟着改变，说明第一层的地址的确不一样\n",
    "list5.Add(new Student() { Name = \"小胖\", Age = 24 });\n",
    "// 当我们修改小潘同学的年龄时，大家都变了，说明真的只是浅拷贝\n",
    "p.Age = 24;\n",
    "\n",
    "foreach (var item in list5)\n",
    "{\n",
    "    Console.WriteLine(item);\n",
    "}\n",
    "Console.WriteLine(\"=============\");\n",
    "foreach (var item in list6)\n",
    "{\n",
    "    Console.WriteLine(item);\n",
    "}\n",
    "```\n",
    "结果：\n",
    "```\n",
    "Name:小张,Age:22\n",
    "Name:小明,Age:23\n",
    "Name:小潘,Age:24\n",
    "Name:小胖,Age:24\n",
    "=============\n",
    "Name:小张,Age:22\n",
    "Name:小明,Age:23\n",
    "Name:小潘,Age:24\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 4.4.简单方式实现深拷贝\n",
    "\n",
    "对于`List<T>`的深拷贝场景，其实项目中还是蛮常见的,那深拷贝怎么搞呢？\n",
    "\n",
    "先来一个简单的实现方式，需要`T`实现`ICloneable`接口才行：\n",
    "\n",
    "定义一个Person类\n",
    "```csharp\n",
    "public partial class Person : ICloneable\n",
    "{\n",
    "    public string Name { get; set; }\n",
    "    public int Age { get; set; }\n",
    "\n",
    "    //实现ICloneable的Clone方法\n",
    "    public object Clone()\n",
    "    {\n",
    "        return base.MemberwiseClone();//调用父类方法即可\n",
    "    }\n",
    "\n",
    "    public override string ToString()\n",
    "    {\n",
    "        return $\"Name:{Name},Age:{Age}\";\n",
    "    }\n",
    "}\n",
    "```\n",
    "给`List<T>`定义一个扩展方法：（温馨提醒：扩展方法所在的类必须是static Class哦）\n",
    "```csharp\n",
    "public static partial class ListExt\n",
    "{\n",
    "    // 只要T实现了ICloneable接口就可以了\n",
    "    public static IEnumerable<T> DeepCopy<T>(this IEnumerable<T> list) where T : ICloneable\n",
    "    {\n",
    "        return list.Select(item => (T)item.Clone()).ToList();\n",
    "    }\n",
    "}\n",
    "```\n",
    "来个调用加验证：\n",
    "```csharp\n",
    "#region 引用类型深拷贝-简单实现方式\n",
    "\n",
    "var oldList = new List<Person>(){\n",
    "    new Person(){Name=\"小明\",Age=23},\n",
    "    new Person(){Name=\"小张\",Age=22},\n",
    "};\n",
    "var xiaoPan = new Person() { Name = \"小潘\", Age = 23 };\n",
    "oldList.Add(xiaoPan);\n",
    "\n",
    "var newList = oldList.DeepCopy();\n",
    "\n",
    "//测试\n",
    "oldList.Add(new Person() { Name = \"小胖\", Age = 23 });\n",
    "xiaoPan.Age = 24;\n",
    "\n",
    "foreach (var item in oldList)\n",
    "{\n",
    "    Console.WriteLine(item);\n",
    "}\n",
    "Console.WriteLine(\"========\");\n",
    "foreach (var item in newList)\n",
    "{\n",
    "    Console.WriteLine(item);\n",
    "}\n",
    "\n",
    "#endregion\n",
    "```\n",
    "结果：\n",
    "```\n",
    "Name:小明,Age:23\n",
    "Name:小张,Age:22\n",
    "Name:小潘,Age:24\n",
    "Name:小胖,Age:23\n",
    "========\n",
    "Name:小明,Age:23\n",
    "Name:小张,Age:22\n",
    "Name:小潘,Age:23\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 4.5.序列化方式实现深拷贝（常用）\n",
    "\n",
    "利用`System.Runtime.Serialization`序列化与反序列化实现深拷贝\n",
    "\n",
    "先定义一个Teacher类（别忘记加 **`Serializable`** 的标签）\n",
    "\n",
    "```csharp\n",
    "[Serializable]\n",
    "public partial class Teacher\n",
    "{\n",
    "    public string Name { get; set; }\n",
    "    public int Age { get; set; }\n",
    "\n",
    "    public override string ToString()\n",
    "    {\n",
    "        return $\"Name:{Name},Age:{Age}\";\n",
    "    }\n",
    "}\n",
    "```\n",
    "添加一个扩展方法：\n",
    "```csharp\n",
    "public static partial class ListExt\n",
    "{\n",
    "    // 利用System.Runtime.Serialization序列化与反序列化实现深拷贝\n",
    "    public static T DeepCopy2<T>(this T obj)\n",
    "    {\n",
    "        using (var stream = new MemoryStream())\n",
    "        {\n",
    "            var formatter = new BinaryFormatter();\n",
    "            formatter.Serialize(stream, obj);\n",
    "            stream.Seek(0, SeekOrigin.Begin);\n",
    "            return (T)formatter.Deserialize(stream);\n",
    "        }\n",
    "    }\n",
    "}\n",
    "```\n",
    "调用：\n",
    "```csharp\n",
    "#region 引用类型深拷贝-序列化实现\n",
    "\n",
    "var oldTestList = new List<Teacher>(){\n",
    "    new Teacher(){Name=\"小明\",Age=23},\n",
    "    new Teacher(){Name=\"小张\",Age=22},\n",
    "};\n",
    "var s = new Teacher() { Name = \"小潘\", Age = 23 };\n",
    "oldTestList.Add(s);\n",
    "\n",
    "var newTestList = oldTestList.DeepCopy2();\n",
    "\n",
    "//测试\n",
    "oldTestList.Add(new Teacher() { Name = \"小胖\", Age = 23 });\n",
    "s.Age = 24;\n",
    "\n",
    "foreach (var item in oldTestList)\n",
    "{\n",
    "    Console.WriteLine(item);\n",
    "}\n",
    "Console.WriteLine(\"========\");\n",
    "foreach (var item in newTestList)\n",
    "{\n",
    "    Console.WriteLine(item);\n",
    "}\n",
    "\n",
    "#endregion\n",
    "```\n",
    "结果：\n",
    "```\n",
    "Name:小明,Age:23\n",
    "Name:小张,Age:22\n",
    "Name:小潘,Age:24\n",
    "Name:小胖,Age:23\n",
    "========\n",
    "Name:小明,Age:23\n",
    "Name:小张,Age:22\n",
    "Name:小潘,Age:23\n",
    "```\n",
    "因为主要是说Python，Net只是简单提一下，这边就先到这里了\n",
    "\n",
    "不尽兴可以看看<a href=\"https://www.cnblogs.com/zhili/p/DeepCopy.html\" target=\"_blank\">这篇文章</a>，讲得还是挺全面的\n",
    "\n",
    "我们接着来对比学习～"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 5.Python生成器\n",
    "\n",
    "一看到标题小明又懵圈了，但是看到大家好像都知道的样子心想道：“我是不是又睡过一节课啊？”\n",
    "\n",
    "<a href=\"#2.2.列表生成式\" target=\"_blank\">之前有讲列表生成式</a>，这边说说生成器\n",
    "\n",
    "通过列表生成式，我们可以简单并直接的创建一个列表，但是当数据有一定的规律而且又很大的时候，使用列表就有点浪费资源了\n",
    "\n",
    "如果列表元素可以按照某种算法推算出来，这样就不必创建完整的list，从而节省大量的资源\n",
    "\n",
    "### 5.1.简单方式\n",
    "\n",
    "**在Python中，这种一边循环一边计算的机制，称为生成器：`generator`**\n",
    "\n",
    "先看一个简单的生成器案例：（**只要把一个列表生成式的`[]`改成`()`** ，就创建了一个`generator`了）"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 69,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]"
      ]
     },
     "execution_count": 69,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 列表生成式\n",
    "[x for x in range(10)]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 70,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "<generator object <genexpr> at 0x7f14c413cb48>"
      ]
     },
     "execution_count": 70,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 生成器写法（Python2.x系列是用xrange）\n",
    "(x for x in range(10))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "遍历方式可以用之前的`for`循环来遍历（推荐）\n",
    "\n",
    "也可以用`next()`或者`__next__()`方法来遍历。【C#是用`MoveNext`】\n",
    "\n",
    "`generator`保存的是算法，每次调用`next(xxx)`或者`__next__()`，就计算出下一个元素的值，直到计算到最后一个元素\n",
    "\n",
    "当没有更多的元素时，抛出`StopIteration`的异常\n",
    "\n",
    "最新的Python3.7在这方面有所优化：<a href=\"https://www.python.org/dev/peps/pep-0479\" target=\"_blank\">https://www.python.org/dev/peps/pep-0479</a>\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 71,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "0\n",
      "1\n",
      "2\n",
      "3\n",
      "4\n",
      "5\n",
      "6\n",
      "7\n",
      "8\n",
      "9\n"
     ]
    }
   ],
   "source": [
    "g=(x for x in range(10))\n",
    "\n",
    "# for来遍历（推荐）\n",
    "for i in g:\n",
    "    print(i)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 72,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "0\n",
      "1\n",
      "2\n",
      "3\n",
      "4\n",
      "5\n",
      "6\n",
      "7\n",
      "8\n",
      "9\n"
     ]
    },
    {
     "ename": "StopIteration",
     "evalue": "",
     "output_type": "error",
     "traceback": [
      "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[0;31mStopIteration\u001b[0m                             Traceback (most recent call last)",
      "\u001b[0;32m<ipython-input-72-9897a9148994>\u001b[0m in \u001b[0;36m<module>\u001b[0;34m()\u001b[0m\n\u001b[1;32m     11\u001b[0m \u001b[0mprint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mnext\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mg\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m     12\u001b[0m \u001b[0mprint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mnext\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mg\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 13\u001b[0;31m \u001b[0mprint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mnext\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mg\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m     14\u001b[0m \u001b[0mprint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mnext\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mg\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
      "\u001b[0;31mStopIteration\u001b[0m: "
     ]
    }
   ],
   "source": [
    "g=(x for x in range(10))\n",
    "\n",
    "print(next(g))\n",
    "print(next(g))\n",
    "print(next(g))\n",
    "print(next(g))\n",
    "print(next(g))\n",
    "print(g.__next__()) #通过__next__也一样取下一个\n",
    "print(next(g))\n",
    "print(next(g))\n",
    "print(next(g))\n",
    "print(next(g))\n",
    "print(next(g))\n",
    "print(next(g))\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 5.2.yield方式\n",
    "\n",
    "如果推算的算法比较复杂，用类似列表生成式的for循环无法实现时，还可以用函数来实现\n",
    "\n",
    "这时候就需要用到`yield`了，像最经典的斐波拉契数列，这次用一波生成器来对比实现下："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 73,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "832040"
      ]
     },
     "execution_count": 73,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 递归方式：求第30个数是多少\n",
    "\n",
    "# 1、1、2、3、5、8、13、21、34...\n",
    "def fib(n):\n",
    "    if n == 1 or n == 2:\n",
    "        return 1\n",
    "    else:\n",
    "        return fib(n - 1) + fib(n - 2)\n",
    "\n",
    "fib(30)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 74,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1\n",
      "1\n",
      "2\n",
      "3\n",
      "5\n",
      "8\n",
      "13\n",
      "21\n",
      "34\n",
      "55\n",
      "89\n",
      "144\n",
      "233\n",
      "377\n",
      "610\n",
      "987\n",
      "1597\n",
      "2584\n",
      "4181\n",
      "6765\n",
      "10946\n",
      "17711\n",
      "28657\n",
      "46368\n",
      "75025\n",
      "121393\n",
      "196418\n",
      "317811\n",
      "514229\n",
      "832040\n"
     ]
    }
   ],
   "source": [
    "# 在讲yield方式之前先用循环实现一下\n",
    "\n",
    "def fibona(max):\n",
    "    n, a, b = 0, 0, 1\n",
    "    while n < max:\n",
    "        print(b)\n",
    "        a, b = b, a + b\n",
    "        n = n + 1\n",
    "\n",
    "fibona(30)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 75,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1\n",
      "1\n",
      "2\n",
      "3\n",
      "5\n",
      "8\n",
      "13\n",
      "21\n",
      "34\n",
      "55\n",
      "89\n",
      "144\n",
      "233\n",
      "377\n",
      "610\n",
      "987\n",
      "1597\n",
      "2584\n",
      "4181\n",
      "6765\n",
      "10946\n",
      "17711\n",
      "28657\n",
      "46368\n",
      "75025\n",
      "121393\n",
      "196418\n",
      "317811\n",
      "514229\n",
      "832040\n"
     ]
    }
   ],
   "source": [
    "# for循环实现\n",
    "\n",
    "def fibona(n):\n",
    "    a, b = 0, 1\n",
    "    # [0,n)\n",
    "    for i in range(n):\n",
    "        print(b)\n",
    "        a, b = b, a + b\n",
    "\n",
    "fibona(30)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "`a, b = b, a + b` 之前交换两数的时候提过\n",
    "\n",
    "这个相当于==> \n",
    "\n",
    "```py\n",
    "temp_tuple = (b, a + b)\n",
    "a = temp_tuple[0]\n",
    "b = temp_tuple[1]\n",
    "```\n",
    "\n",
    "要把`fibona`函数变成`generator`，只需要把`print(b)`改为`yield b`就可以了：\n",
    "\n",
    "**`generator`在执行过程中，遇到`yield`就中断，下次又继续执行到`yield`停下了，一直到最后**\n",
    "\n",
    "生成器的特点：\n",
    "\n",
    "1. 节约内存\n",
    "2. 迭代到下一次的调用时，所使用的参数都是第一次所保留下的（所有函数调用的参数都是第一次所调用时保留的，而不是新创建的）"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 76,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 改成生成器比较简单，直接换输出为yield\n",
    "\n",
    "def fibona(n):\n",
    "    a, b = 0, 1\n",
    "    # [0,n)\n",
    "    for i in range(n):\n",
    "        yield b\n",
    "        a, b = b, a + b"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 77,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "<generator object fibona at 0x7f14c40efd58>"
      ]
     },
     "execution_count": 77,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 看看是不是生成器\n",
    "g = fibona(30)\n",
    "\n",
    "g"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 78,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1\n",
      "1\n",
      "2\n",
      "3\n",
      "5\n",
      "8\n",
      "13\n",
      "21\n",
      "34\n",
      "55\n",
      "89\n",
      "144\n",
      "233\n",
      "377\n",
      "610\n",
      "987\n",
      "1597\n",
      "2584\n",
      "4181\n",
      "6765\n",
      "10946\n",
      "17711\n",
      "28657\n",
      "46368\n",
      "75025\n",
      "121393\n",
      "196418\n",
      "317811\n",
      "514229\n",
      "832040\n"
     ]
    }
   ],
   "source": [
    "# 遍历输出（基本上都会用for来遍历）\n",
    "for i in g:\n",
    "    print(i)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "对于函数改成的generator来说，遇到return语句或者执行到函数体最后一行语句，就是结束generator的循环的时候\n",
    "\n",
    "小明总结如下：\n",
    "\n",
    "1. **在Python中，这种一边循环一边计算的机制称为生成器：generator**\n",
    "\n",
    "2. **每一个生成器都是一个迭代器（迭代器不一定是生成器）**\n",
    "\n",
    "3. **如果一个函数包含yield关键字，这个函数就会变为一个生成器**\n",
    "\n",
    "4. **生成器并不会一次返回所有结果，而是每次遇到yield关键字后返回相应结果，并保留函数当前的运行状态，等待下一次的调用**\n",
    "\n",
    "5. **由于生成器也是一个迭代器，那么它就支持next用方法来获取下一个值（我们平时用for来遍历它）**\n",
    "\n",
    "推荐一篇文章，总结的很全了：（<a href=\"https://www.cnblogs.com/python-life/articles/4549996.html\" target=\"_blank\">yield用法总结</a>）\n",
    "\n",
    "### 5.3.扩展之～`send(msg)`方法：\n",
    "\n",
    "其实`__next__()`和`send()`在一定意义上作用是相似的，区别是`send()`可以传递yield表达式的值进去\n",
    "\n",
    "而`__next__()`不 能传递特定的值。我们可以看做`x.__next__()` 和 `x.send(None)` 作用是一样的"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 79,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "<generator object test_send at 0x7f14c40efdb0>"
      ]
     },
     "execution_count": 79,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 来个案例：\n",
    "def test_send(n):\n",
    "    for i in range(n):\n",
    "        tmp = yield i\n",
    "        print(tmp)\n",
    "\n",
    "\n",
    "g = test_send(5)\n",
    "\n",
    "g"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 80,
   "metadata": {
    "scrolled": true
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[0]\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "1"
      ]
     },
     "execution_count": 80,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 定义一个列表\n",
    "test_list = []\n",
    "\n",
    "# 把第一次yield的值放在列表中\n",
    "test_list.append(g.__next__())\n",
    "\n",
    "# 把list传给tmp并打印（可以理解为把表达式右边的 yield i 暂时换成了 test_list）\n",
    "# out的内容是yield返回的值\n",
    "g.send(test_list)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 81,
   "metadata": {
    "scrolled": true
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "你好啊\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "2"
      ]
     },
     "execution_count": 81,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 以防你们看不懂，来个简单案例\n",
    "# 你传啥print(tmp)就给你打印啥\n",
    "g.send(\"你好啊\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "注意一种情况，`generator`刚启动的时候，要么不传，要么只能传`None`\n",
    "\n",
    "解决：要么一开始`send(None)`要么一开始先调用一下`__next()__` or `next()`"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 82,
   "metadata": {},
   "outputs": [
    {
     "ename": "TypeError",
     "evalue": "can't send non-None value to a just-started generator",
     "output_type": "error",
     "traceback": [
      "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[0;31mTypeError\u001b[0m                                 Traceback (most recent call last)",
      "\u001b[0;32m<ipython-input-82-2e891aa5dd81>\u001b[0m in \u001b[0;36m<module>\u001b[0;34m()\u001b[0m\n\u001b[1;32m      7\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m      8\u001b[0m \u001b[0mg\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mtest_send\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;36m5\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 9\u001b[0;31m \u001b[0mg\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0msend\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"dog\"\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;31m# TypeError: can't send non-None value to a just-started generator\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m",
      "\u001b[0;31mTypeError\u001b[0m: can't send non-None value to a just-started generator"
     ]
    }
   ],
   "source": [
    "# 注意一种情况，generator刚启动的时候，要么不传，要么只能传None\n",
    "def test_send(n):\n",
    "    for i in range(n):\n",
    "        tmp = yield i\n",
    "        print(tmp)\n",
    "\n",
    "\n",
    "g = test_send(5)\n",
    "g.send(\"dog\") # TypeError: can't send non-None value to a just-started generator"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 83,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "0"
      ]
     },
     "execution_count": 83,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 解决：要么一开始send(None)要么一开始先调用一下__next()__ or next()\n",
    "def test_send(n):\n",
    "    for i in range(n):\n",
    "        tmp = yield i\n",
    "        print(tmp)\n",
    "\n",
    "\n",
    "g = test_send(5)\n",
    "g.send(None)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 84,
   "metadata": {
    "scrolled": true
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "dog\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "1"
      ]
     },
     "execution_count": 84,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "g.send(\"dog\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "扩：C#在遍历`generator`的时候也是先调一下`MoveNext`方法\n",
    "\n",
    "```csharp\n",
    "while (tmp.MoveNext())\n",
    "{\n",
    "    Console.WriteLine(tmp.Current);\n",
    "}\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 5.4.扩展之～`return`和`break`的说明\n",
    "\n",
    "在一个`generator`函数中，如果没有`return`则默认执行至函数完毕\n",
    "\n",
    "如果在执行过程中`return`或者`break`则直接抛出`StopIteration`终止迭代"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 85,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "0\n",
      "1\n"
     ]
    }
   ],
   "source": [
    "# break案例\n",
    "def test_send(n):\n",
    "    for i in range(n):\n",
    "        if i==2:\n",
    "            break\n",
    "        yield i\n",
    "\n",
    "g = test_send(5)\n",
    "for i in g:\n",
    "    print(i)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 86,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "0\n",
      "1\n"
     ]
    }
   ],
   "source": [
    "# return案例\n",
    "def test_send(n):\n",
    "    for i in range(n):\n",
    "        if i==2:\n",
    "            return \"i==2\"\n",
    "        yield i\n",
    "\n",
    "g = test_send(5)\n",
    "for i in g:\n",
    "    print(i)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**用`for`循环调用`generator`时，发现拿不到`generator`的`return`语句的返回值**\n",
    "\n",
    "**如果想要拿到返回值，必须捕获`StopIteration`错误，返回值包含在`StopIteration的value`中**"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 87,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "0\n",
      "1\n",
      "i==2\n"
     ]
    }
   ],
   "source": [
    "# 上面return的返回值怎么拿呢？\n",
    "\n",
    "g = test_send(5)\n",
    "\n",
    "while True:\n",
    "    try:\n",
    "        tmp = g.__next__()\n",
    "        print(tmp)\n",
    "    except StopIteration as ex:\n",
    "        print(ex.value)\n",
    "        break # 一定要加break，别忘了你在死循环里呢"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 5.5.扩展之～协程`yield`实现多任务调度\n",
    "\n",
    "这个场景还是很常见的，比如C#的单线程实现多任务用的就可以使用yield\n",
    "\n",
    "再比如**生产消费**这个经典案例：（<a href=\"https://www.cnblogs.com/fangyuan1004/p/4571304.html\" target=\"_blank\">参考</a>）\n",
    "\n",
    "**生产者生产消息后，直接通过yield跳转到消费者开始执行，待消费者执行完毕后，切换回生产者继续生产**\n",
    "\n",
    "Python对协程的支持是通过generator实现的\n",
    "\n",
    "在generator中，我们不但可以通过for循环来迭代，还可以不断调用`__next__()`获取由`yield`语句返回的下一个值。\n",
    "\n",
    "因为Python的`yield`**不但可以返回一个值，它还可以接收调用者发出的参数**(通过`send`方法),所以就happy了\n",
    "\n",
    "我们举个简单的demo来看看："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 88,
   "metadata": {},
   "outputs": [],
   "source": [
    "def consumer():\n",
    "    while True:\n",
    "        tmp = yield\n",
    "        # !None就变成真了\n",
    "        if not tmp:\n",
    "            return\n",
    "        print(\"消费者:\",tmp)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 89,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "消费者: 小明\n",
      "消费者: 小潘\n"
     ]
    },
    {
     "ename": "StopIteration",
     "evalue": "",
     "output_type": "error",
     "traceback": [
      "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[0;31mStopIteration\u001b[0m                             Traceback (most recent call last)",
      "\u001b[0;32m<ipython-input-89-bcc0083d4089>\u001b[0m in \u001b[0;36m<module>\u001b[0;34m()\u001b[0m\n\u001b[1;32m      7\u001b[0m \u001b[0mc\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0msend\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"小潘\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m      8\u001b[0m \u001b[0;31m# 生产结束，通知消费者结束，抛出StopIteration异常\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 9\u001b[0;31m \u001b[0mc\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0msend\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;32mNone\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;31m# 使用c.close()可以避免异常\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m",
      "\u001b[0;31mStopIteration\u001b[0m: "
     ]
    }
   ],
   "source": [
    "# 创建消费者\n",
    "c = consumer()\n",
    "# 启动消费者\n",
    "c.send(None)\n",
    "# 生产数据，并提交给消费者\n",
    "c.send(\"小明\")\n",
    "c.send(\"小潘\")\n",
    "# 生产结束，通知消费者结束，抛出StopIteration异常\n",
    "c.send(None) # 使用c.close()可以避免异常"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**执行流程**： \n",
    "\n",
    "1. 创建协程对象（消费者）后，必须使用`send(None)`或`__next__()`启动\n",
    "2. 协程在执行yield后让出执行绪，等待消息\n",
    "3. 调用方发送`send(msg)`消息，协程恢复执行，将接收到的数据保存并执行后续流程\n",
    "4. 再次循环到yield，协程返回前面的处理结果，并再次让出执行绪\n",
    "5. 直到关闭或被引发异常\n",
    "\n",
    "补全demo："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 90,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "生产者：出产商品1号...\n",
      "消费者：获得商品1号...\n",
      "生产者：生产者消费状态: ok\n",
      "生产者：出产商品2号...\n",
      "消费者：获得商品2号...\n",
      "生产者：生产者消费状态: ok\n"
     ]
    }
   ],
   "source": [
    "def consumer():\n",
    "    status = \"\"\n",
    "    while True:\n",
    "        tmp = yield status\n",
    "        if not tmp:\n",
    "            print(\"消费者已经睡觉了...\")\n",
    "            return\n",
    "        print(\"消费者：获得商品%s号...\" % tmp)\n",
    "        status = \"ok\"\n",
    "\n",
    "\n",
    "def produce(c):\n",
    "    # 启动消费者\n",
    "    c.send(None)\n",
    "    for i in range(1, 3):\n",
    "        print(\"生产者：出产商品%s号...\" % i)\n",
    "        # 生产商品，并提交给消费者\n",
    "        status = c.send(i)\n",
    "        print(\"生产者：生产者消费状态: %s\" % status)\n",
    "    # c.send(None) 执行这个会引发StopIteration\n",
    "    c.close()  # 使用close就可以避免了(手动关闭生成器函数，后面的调用会直接返回StopIteration异常)\n",
    "\n",
    "\n",
    "# 创建消费者\n",
    "c = consumer()\n",
    "produce(c)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 91,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Help on generator object:\n",
      "\n",
      "test = class generator(object)\n",
      " |  Methods defined here:\n",
      " |  \n",
      " |  __del__(...)\n",
      " |  \n",
      " |  __getattribute__(self, name, /)\n",
      " |      Return getattr(self, name).\n",
      " |  \n",
      " |  __iter__(self, /)\n",
      " |      Implement iter(self).\n",
      " |  \n",
      " |  __next__(self, /)\n",
      " |      Implement next(self).\n",
      " |  \n",
      " |  __repr__(self, /)\n",
      " |      Return repr(self).\n",
      " |  \n",
      " |  close(...)\n",
      " |      close() -> raise GeneratorExit inside generator.\n",
      " |  \n",
      " |  send(...)\n",
      " |      send(arg) -> send 'arg' into generator,\n",
      " |      return next yielded value or raise StopIteration.\n",
      " |  \n",
      " |  throw(...)\n",
      " |      throw(typ[,val[,tb]]) -> raise exception in generator,\n",
      " |      return next yielded value or raise StopIteration.\n",
      " |  \n",
      " |  ----------------------------------------------------------------------\n",
      " |  Data descriptors defined here:\n",
      " |  \n",
      " |  gi_code\n",
      " |  \n",
      " |  gi_frame\n",
      " |  \n",
      " |  gi_running\n",
      " |  \n",
      " |  gi_yieldfrom\n",
      " |      object being iterated by yield from, or None\n",
      "\n"
     ]
    }
   ],
   "source": [
    "# 更多可以查看帮助文档\n",
    "def test():\n",
    "    yield\n",
    "help(test())"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 6.Python迭代器\n",
    "\n",
    "看到迭代器小明老高兴了，心想着一会写个C#版的觉得可以收获一大群眼球～\n",
    "\n",
    "### 6.1.判断是否可迭代\n",
    "\n",
    "在说迭代器前先说下**可迭代(Iterable)**（<a href=\"#5.2.yield方式\" target=\"_blank\">yield基础点我</a>）：\n",
    "\n",
    "在Python中，能通过for循环遍历的都是可以迭代的，比如 **`str、tuple、list、dict、set、生成器`**等等\n",
    "\n",
    "也可以通过 **`isinstance(xxx,Iterable)`** 方法判断一下是否迭代："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 92,
   "metadata": {},
   "outputs": [],
   "source": [
    "from collections import Iterable"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 93,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "True"
      ]
     },
     "execution_count": 93,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "isinstance(\"mmd\",Iterable)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 94,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "True"
      ]
     },
     "execution_count": 94,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "isinstance((1,2),Iterable)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 95,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "True"
      ]
     },
     "execution_count": 95,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "isinstance([],Iterable)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 96,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "True"
      ]
     },
     "execution_count": 96,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "isinstance({},Iterable)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 97,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "True"
      ]
     },
     "execution_count": 97,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "isinstance((x for x in range(10)),Iterable)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 98,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "False"
      ]
     },
     "execution_count": 98,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "isinstance(1,Iterable)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 6.2.判断是否是迭代器\n",
    "\n",
    "迭代器是一定可以迭代的，怎么判断是迭代器呢？\n",
    "\n",
    "可以使用`next`方法的或者通过`isinstance(xxx,Iterator)`"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 99,
   "metadata": {},
   "outputs": [
    {
     "ename": "TypeError",
     "evalue": "'list' object is not an iterator",
     "output_type": "error",
     "traceback": [
      "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[0;31mTypeError\u001b[0m                                 Traceback (most recent call last)",
      "\u001b[0;32m<ipython-input-99-f5f8ac9a8550>\u001b[0m in \u001b[0;36m<module>\u001b[0;34m()\u001b[0m\n\u001b[1;32m      1\u001b[0m \u001b[0ma\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m1\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;36m2\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;36m3\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m      2\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 3\u001b[0;31m \u001b[0mnext\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0ma\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m",
      "\u001b[0;31mTypeError\u001b[0m: 'list' object is not an iterator"
     ]
    }
   ],
   "source": [
    "a=[1,2,3]\n",
    "\n",
    "next(a)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 100,
   "metadata": {},
   "outputs": [],
   "source": [
    "from collections import Iterator"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 101,
   "metadata": {
    "scrolled": true
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "False"
      ]
     },
     "execution_count": 101,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "isinstance([],Iterator)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 102,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "True"
      ]
     },
     "execution_count": 102,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "isinstance((x for x in range(10)),Iterator)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 6.3.`Iterable` 转 `Iterator`\n",
    "\n",
    "生成器都是`Iterator`对象，但`list、dict、str`虽然是`Iterable`，却不是`Iterator`\n",
    "\n",
    "把`list、dict、str`等`Iterable`变成`Iterator`可以使用**`iter()`**函数："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 103,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "<list_iterator at 0x7f14c40a3da0>"
      ]
     },
     "execution_count": 103,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "iter(a)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 104,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "True"
      ]
     },
     "execution_count": 104,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "isinstance(iter([]),Iterator)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 105,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "True"
      ]
     },
     "execution_count": 105,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "isinstance(iter({}),Iterator)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Python的`Iterator`对象表示的是一个数据流，`Iterator`对象可以被`next()`or`__next__()`函数调用并不断返回下一个数据,直到没有数据时抛出`StopIteration`错误\n",
    "\n",
    "可以把这个数据流看做是一个有序序列，但我们却不能提前知道序列的长度，只能不断通过`next`函数实现按需计算下一个数据，所以`Iterator`的计算是惰性的，只有在需要返回下一个数据时它才会计算。\n",
    "\n",
    "`Iterator`甚至可以表示一个无限大的数据流，而`list`等则不行\n",
    "\n",
    "小明总结了一下老师讲解的知识点：\n",
    "\n",
    "1. **可以`for`循环的对象都是`Iterable`类型**\n",
    "\n",
    "2. **可以使用`next()`or`__next__()`函数的对象都是`Iterator`类型**\n",
    "\n",
    "3. **集合数据类型如list、dict、str等是`Iterable`，可以通过`iter()`函数获得一个`Iterator`对象**"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 7.CSharp迭代器\n",
    "\n",
    "乘着下课的时间，小明跑到黑板前，心想：“又到了C#的时候了，看我来收播一大群眼球～”，然后开始了他的个人秀：\n",
    "\n",
    "其实**迭代器（`iterator`）**就是为了更简单的创建枚举器（`enumerator`）和可枚举类型（`enumerator type`）的方式\n",
    "\n",
    "### 7.1.`IEnumerator` 和 `IEnumerable`\n",
    "\n",
    "通俗话讲：\n",
    "\n",
    "**能不能`foreach`就看你遍历对象有没有实现`IEnumerable`，就说明你是不是一个`可枚举类型`（`enumerator type`）**\n",
    "\n",
    "```csharp\n",
    "public interface IEnumerable\n",
    "{\n",
    "    IEnumerator GetEnumerator();\n",
    "}\n",
    "```\n",
    "**是不是个枚举器（`enumerator`）就看你实现了`IEnumerator`接口没**\n",
    "```csharp\n",
    "public interface IEnumerator\n",
    "{\n",
    "    object Current { get; }\n",
    "\n",
    "    bool MoveNext();\n",
    "\n",
    "    void Reset();\n",
    "}\n",
    "```\n",
    "**最明显的区别：它们两个遍历方式不一样**\n",
    "```csharp\n",
    "// 枚举器遍历\n",
    "var tmp = FibonaByIEnumerator(30);\n",
    "while (tmp.MoveNext())\n",
    "{\n",
    "    Console.WriteLine(tmp.Current);\n",
    "}\n",
    "// 可枚举类型遍历\n",
    "foreach (var item in FibonaByIEnumerable(30))\n",
    "{\n",
    "    Console.WriteLine(item);\n",
    "}\n",
    "```\n",
    "\n",
    "---\n",
    "\n",
    "这个我们在2年前就说过，这边简单提一下（<a href=\"https://docs.microsoft.com/zh-cn/dotnet/api/system.collections.ienumerator?view=netcore-2.1\" target=\"_blank\">官方文档</a>）(<a href=\"https://github.com/lotapp/BaseCode/tree/master/netcore/3_Ext/Enumerator\" target=\"_blank\">Demo</a>)\n",
    "\n",
    "MyEnumerator文件：\n",
    "```csharp\n",
    "public class MyEnumerator : IEnumerator\n",
    "{\n",
    "    /// <summary>\n",
    "    /// 需要遍历的数组\n",
    "    /// </summary>\n",
    "    private string[] array;\n",
    "    /// <summary>\n",
    "    /// 有效数的个数\n",
    "    /// </summary>\n",
    "    private int count;\n",
    "    public MyEnumerator(string[] array, int count)\n",
    "    {\n",
    "        this.array = array;\n",
    "        this.count = count;\n",
    "    }\n",
    "\n",
    "    /// <summary>\n",
    "    /// 当前索引（线moveNext再获取index，用-1更妥）\n",
    "    /// </summary>\n",
    "    private int index = -1;\n",
    "    public object Current\n",
    "    {\n",
    "        get\n",
    "        {\n",
    "            return array[index];\n",
    "        }\n",
    "    }\n",
    "    /// <summary>\n",
    "    /// 移位\n",
    "    /// </summary>\n",
    "    /// <returns></returns>\n",
    "    public bool MoveNext()\n",
    "    {\n",
    "        if (++index < count)\n",
    "        {\n",
    "            return true;\n",
    "        }\n",
    "        return false;\n",
    "    }\n",
    "    /// <summary>\n",
    "    /// 重置\n",
    "    /// </summary>\n",
    "    public void Reset()\n",
    "    {\n",
    "        index = -1;\n",
    "    }\n",
    "}\n",
    "```\n",
    "MyArray.cs文件\n",
    "```csharp\n",
    "public partial class MyArray\n",
    "{\n",
    "    /// <summary>\n",
    "    /// 数组容量\n",
    "    /// </summary>\n",
    "    private string[] array = new string[4];\n",
    "    /// <summary>\n",
    "    /// 数组元素个数\n",
    "    /// </summary>\n",
    "    private int count = 0;\n",
    "    /// <summary>\n",
    "    /// 当前数组的长度\n",
    "    /// </summary>\n",
    "    public int Length\n",
    "    {\n",
    "        get\n",
    "        {\n",
    "            return count;\n",
    "        }\n",
    "    }\n",
    "\n",
    "    /// <summary>\n",
    "    /// 添加元素\n",
    "    /// </summary>\n",
    "    /// <param name=\"str\"></param>\n",
    "    /// <returns></returns>\n",
    "    public MyArray Add(string str)\n",
    "    {\n",
    "        //要溢出的时候扩容\n",
    "        if (count == array.Length)\n",
    "        {\n",
    "            string[] newArray = new string[2 * array.Length];\n",
    "            array.CopyTo(newArray, 0);\n",
    "            array = newArray;//array重新指向\n",
    "        }\n",
    "        array[count++] = str;\n",
    "        return this;\n",
    "    }\n",
    "\n",
    "    /// <summary>\n",
    "    /// 移除某一项\n",
    "    /// </summary>\n",
    "    /// <param name=\"i\"></param>\n",
    "    /// <returns></returns>\n",
    "    public MyArray RemoveAt(int i)\n",
    "    {\n",
    "        for (int j = i; j < count - 1; j++)\n",
    "        {\n",
    "            array[j] = array[j + 1];\n",
    "        }\n",
    "        count--;//少了一个元素所以--\n",
    "        return this;\n",
    "    }\n",
    "\n",
    "    /// <summary>\n",
    "    /// 索引器\n",
    "    /// </summary>\n",
    "    /// <param name=\"index\"></param>\n",
    "    /// <returns></returns>\n",
    "    public string this[int index]\n",
    "    {\n",
    "        get\n",
    "        {\n",
    "            return array[index];\n",
    "        }\n",
    "        set\n",
    "        {\n",
    "            array[index] = value;\n",
    "        }\n",
    "    }\n",
    "}\n",
    "```\n",
    "MyArrayExt.cs文件:\n",
    "```csharp\n",
    "public partial class MyArray: IEnumerable\n",
    "{\n",
    "    /// <summary>\n",
    "    /// 枚举器方法\n",
    "    /// </summary>\n",
    "    /// <returns></returns>\n",
    "    public IEnumerator GetEnumerator()\n",
    "    {\n",
    "        return new MyEnumerator(this.array, this.count);\n",
    "    }\n",
    "}\n",
    "```\n",
    "调用:\n",
    "```csharp\n",
    "static void Main(string[] args)\n",
    "{\n",
    "    MyArray array = new MyArray();\n",
    "    array.Add(\"~\").Add(\"这\").Add(\"是\").Add(\"一\").Add(\"个\").Add(\"测\").Add(\"试\").Add(\"。\").RemoveAt(0).RemoveAt(3).RemoveAt(6);\n",
    "    for (int i = 0; i < array.Length; i++)\n",
    "    {\n",
    "        Console.Write(array[i]);\n",
    "    }\n",
    "    Console.WriteLine();\n",
    "    foreach (var item in array)\n",
    "    {\n",
    "        Console.Write(item);\n",
    "    }\n",
    "}\n",
    "```\n",
    "结果:\n",
    "```\n",
    "这是一测试\n",
    "这是一测试\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 7.2.yield方式\n",
    "\n",
    "小明看着班里女生羡慕的眼神，得意的强调道：\n",
    "\n",
    "注意一下，C#是用`yield return xxx`，Python是用`yield xxx`关键字\n",
    "\n",
    "还记得开头说的那句话吗？（<a href=\"https://docs.microsoft.com/zh-cn/dotnet/csharp/language-reference/keywords/yield\" target=\"_blank\">yield官方文档</a>）\n",
    "\n",
    ">其实迭代器（iterator）就是为了更简单的创建枚举器（enumerator）和可枚举类型（enumerator type）的方式\n",
    "\n",
    "如果**枚举器和可枚举类型**还是不理解（<a href=\"https://github.com/lotapp/BaseCode/tree/master/netcore/3_Ext/IteratorDemo\" target=\"_blank\">举个例子</a>）就懂了：（从遍历方式就看出区别了）\n",
    "\n",
    "定义一个斐波拉契函数，返回可枚举类型\n",
    "```csharp\n",
    "/// <summary>\n",
    "/// 返回一个可枚举类型\n",
    "/// </summary>\n",
    "public static IEnumerable<int> FibonaByIEnumerable(int n)\n",
    "{\n",
    "    int a = 0;\n",
    "    int b = 1;\n",
    "    for (int i = 0; i < n; i++)\n",
    "    {\n",
    "        yield return b;\n",
    "        (a, b) = (b, a + b);\n",
    "    }\n",
    "}\n",
    "```\n",
    "调用：\n",
    "```csharp\n",
    "foreach (var item in FibonaByIEnumerable(30))\n",
    "{\n",
    "    Console.WriteLine(item);\n",
    "}\n",
    "```\n",
    "\n",
    "---\n",
    "\n",
    "定义一个斐波拉契函数，返回一个枚举器\n",
    "```csharp\n",
    "/// <summary>\n",
    "/// 返回一个枚举器\n",
    "/// </summary>\n",
    "public static IEnumerator<int> FibonaByIEnumerator(int n)\n",
    "{\n",
    "    int a = 0;\n",
    "    int b = 1;\n",
    "    for (int i = 0; i < n; i++)\n",
    "    {\n",
    "        yield return b;\n",
    "        (a, b) = (b, a + b);\n",
    "    }\n",
    "}\n",
    "```\n",
    "调用一下：\n",
    "```csharp\n",
    "var tmp = FibonaByIEnumerator(30);\n",
    "while (tmp.MoveNext())\n",
    "{\n",
    "    Console.WriteLine(tmp.Current);\n",
    "}\n",
    "```\n",
    "利用`yield`轻轻松松就创建了`枚举器和可枚举类型`\n",
    "\n",
    "以上面那个MyArray的案例来说，有了yield我们代码量大大简化：（<a href=\"https://github.com/lotapp/BaseCode/tree/master/netcore/3_Ext/simpleDemo\" target=\"_blank\">Demo</a>）\n",
    "\n",
    "MyArray.cs\n",
    "```csharp\n",
    "public partial class MyArray\n",
    "{\n",
    "    /// <summary>\n",
    "    /// 数组容量\n",
    "    /// </summary>\n",
    "    private string[] array = new string[4];\n",
    "    /// <summary>\n",
    "    /// 数组元素个数\n",
    "    /// </summary>\n",
    "    private int count = 0;\n",
    "    /// <summary>\n",
    "    /// 当前数组的长度\n",
    "    /// </summary>\n",
    "    public int Length\n",
    "    {\n",
    "        get\n",
    "        {\n",
    "            return count;\n",
    "        }\n",
    "    }\n",
    "\n",
    "    /// <summary>\n",
    "    /// 添加元素\n",
    "    /// </summary>\n",
    "    /// <param name=\"str\"></param>\n",
    "    /// <returns></returns>\n",
    "    public MyArray Add(string str)\n",
    "    {\n",
    "        //要溢出的时候扩容\n",
    "        if (count == array.Length)\n",
    "        {\n",
    "            string[] newArray = new string[2 * array.Length];\n",
    "            array.CopyTo(newArray, 0);\n",
    "            array = newArray;//array重新指向\n",
    "        }\n",
    "        array[count++] = str;\n",
    "        return this;\n",
    "    }\n",
    "\n",
    "    /// <summary>\n",
    "    /// 移除某一项\n",
    "    /// </summary>\n",
    "    /// <param name=\"i\"></param>\n",
    "    /// <returns></returns>\n",
    "    public MyArray RemoveAt(int i)\n",
    "    {\n",
    "        for (int j = i; j < count - 1; j++)\n",
    "        {\n",
    "            array[j] = array[j + 1];\n",
    "        }\n",
    "        array[count - 1] = string.Empty;//add 干掉移除的数组\n",
    "        count--;//少了一个元素所以--\n",
    "        return this;\n",
    "    }\n",
    "\n",
    "    /// <summary>\n",
    "    /// 索引器\n",
    "    /// </summary>\n",
    "    /// <param name=\"index\"></param>\n",
    "    /// <returns></returns>\n",
    "    public string this[int index]\n",
    "    {\n",
    "        get\n",
    "        {\n",
    "            return array[index];\n",
    "        }\n",
    "        set\n",
    "        {\n",
    "            array[index] = value;\n",
    "        }\n",
    "    }\n",
    "}\n",
    "```\n",
    "MyArrayExt.cs\n",
    "```csharp\n",
    "public partial class MyArray : IEnumerable\n",
    "{\n",
    "    /// <summary>\n",
    "    /// 枚举器方法\n",
    "    /// </summary>\n",
    "    /// <returns></returns>\n",
    "    public IEnumerator GetEnumerator()\n",
    "    {\n",
    "        return MyEnumerator();\n",
    "    }\n",
    "    /// <summary>\n",
    "    /// 通过yield快速实现\n",
    "    /// </summary>\n",
    "    /// <returns></returns>\n",
    "    public IEnumerator<string> MyEnumerator()\n",
    "    {\n",
    "        foreach (var item in this.array)\n",
    "        {\n",
    "            yield return item;\n",
    "        }\n",
    "    }\n",
    "}\n",
    "```\n",
    "然后就行了，MyEnumerator都不用你实现了：\n",
    "```csharp\n",
    "MyArray array = new MyArray();\n",
    "array.Add(\"~\").Add(\"这\").Add(\"是\").Add(\"一\").Add(\"个\").Add(\"测\").Add(\"试\").Add(\"。\").RemoveAt(0).RemoveAt(3).RemoveAt(6);\n",
    "for (int i = 0; i < array.Length; i++)\n",
    "{\n",
    "    Console.Write(array[i]);\n",
    "}\n",
    "Console.WriteLine();\n",
    "foreach (var item in array)\n",
    "{\n",
    "    Console.Write(item);\n",
    "}\n",
    "```\n",
    "结果：\n",
    "```\n",
    "这是一测试\n",
    "这是一测试\n",
    "```\n",
    "\n",
    "---\n",
    "\n",
    "扩充一下：`Python`退出迭代器用`yield return` 或者 `yield break`，**C#使用`yield break`来退出迭代**\n",
    "\n",
    "做个 <a href=\"https://github.com/lotapp/BaseCode/tree/master/netcore/3_Ext/TestBreak\" target=\"_blank\">demo</a> 测试下:\n",
    "```csharp\n",
    "public static IEnumerable<int> GetValue()\n",
    "{\n",
    "    for (int i = 0; i < 5; i++)\n",
    "    {\n",
    "        yield return i;\n",
    "        if (i == 2)\n",
    "        {\n",
    "            yield break;\n",
    "        }\n",
    "    }\n",
    "}\n",
    "```\n",
    "调用:\n",
    "```csharp\n",
    "static void Main(string[] args)\n",
    "{\n",
    "    foreach (var item in GetValue())\n",
    "    {\n",
    "        Console.WriteLine(item);\n",
    "    }\n",
    "}\n",
    "```\n",
    "输出:\n",
    "```\n",
    "0\n",
    "1\n",
    "2\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 8.闭包\n",
    "\n",
    "### 8.1.Python闭包\n",
    "\n",
    "又到了上课时间，小明灰溜溜的跑回座位，听老师讲起了闭包的知识：\n",
    "\n",
    "函数方面还有不懂的可以看之前讲的文档：<a href=\"https://www.cnblogs.com/dotnetcrazy/p/9175950.html\" target=\"_blank\">Function Base</a>\n",
    "\n",
    "**函数除了可以<a href=\"https://www.cnblogs.com/dotnetcrazy/p/9175950.html#4.3.匿名函数\" target=\"_blank\">接受函数作为参数</a>外，还可以把函数作为结果值返回**（有点类似于C++里面的函数指针了）\n",
    "\n",
    "来看一个可变参数求和的例子："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "<function slow_sum.<locals>.get_sum at 0x7f57783b6268>\n",
      "15\n"
     ]
    }
   ],
   "source": [
    "def slow_sum(*args):\n",
    "    def get_sum():\n",
    "        sum = 0\n",
    "        for i in args:\n",
    "            sum += i\n",
    "        return sum\n",
    "\n",
    "    return get_sum  # 返回函数引用地址（不加括号）\n",
    "\n",
    "a = slow_sum(1, 2, 3, 4, 5)# 返回get_sum函数的引用\n",
    "print(a)# 看看引用地址\n",
    "print(a())# a() 这时候才是调用get_sum()函数"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "其实上面一个案例就是闭包(`Closure`)了，来个定义：\n",
    "\n",
    "**在函数内部再定义一个函数，并且这个函数用到了外边函数的变量**(`参数`或者`局部变量`)，**那么将这个函数以及用到的一些变量称之为闭包**\n",
    "\n",
    "通俗点说就是：`内部函数使用了外部函数作用域里的变量了，那这个内部函数和它用到的变量就是个闭包`\n",
    "\n",
    "注意：当我们调用`slow_sum()`时，**每次调用都会返回一个新的函数**（相同的参数也一样）"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "False"
      ]
     },
     "execution_count": 2,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "a = slow_sum(1, 2, 3, 4)\n",
    "b = slow_sum(1, 2, 3, 4)\n",
    "\n",
    "a is b\n",
    "\n",
    "# a()和b()的调用结果互不影响"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**由于闭包引用了外部函数的局部变量，则外部函数的局部变量没有及时释放，所以也容易消耗内存**\n",
    "\n",
    "so ==> **`除非你真正需要它,否则不要使用闭包`**\n",
    "\n",
    "**返回函数尽量不要引用任何循环变量，或者后续会发生变化的变量（容易出错）**\n",
    "\n",
    "看着小明一脸懵圈的样子，老师说道：\n",
    "\n",
    "新讲的知识点一般都不太容易快速消化，我们再来看个闭包的好处就理解了：\n",
    "\n",
    "比如现在我们要根据公式来求解，以`y=ax+b`为例，传统方法解决："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 定义一个y=ax+b的函数公式\n",
    "def get_value(a, b, x):\n",
    "    return a * x + b"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "3\n",
      "5\n",
      "7\n",
      "9\n"
     ]
    }
   ],
   "source": [
    "# 每次调用都得传 a,b\n",
    "print(get_value(2, 1, 1))\n",
    "print(get_value(2, 1, 2))\n",
    "print(get_value(2, 1, 3))\n",
    "print(get_value(2, 1, 4))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "每次调用都得额外传`a、b`的值\n",
    "\n",
    "就算使用**偏函数**来简化也不合适（毕竟已经是一个新的函数了）："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {
    "scrolled": true
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "3\n",
      "5\n",
      "7\n",
      "9\n",
      "11\n"
     ]
    }
   ],
   "source": [
    "from functools import partial\n",
    "\n",
    "new_get_value = partial(get_value, 2, 1)\n",
    "\n",
    "print(new_get_value(1))\n",
    "print(new_get_value(2))\n",
    "print(new_get_value(3))\n",
    "print(new_get_value(4))\n",
    "print(new_get_value(5))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "简单总结`functools.partial`的作用就是：\n",
    "\n",
    "> **把一个函数的某些参数设置默认值，返回一个新的函数，然后调用新函数就免得你再输入重复参数了**\n",
    "\n",
    "而这时候使用闭包就比较合适了，而且真的是封装了一个通用公式了\n",
    "\n",
    "a，b的值你可以任意变来生成新的公式，而且公式之间还不干扰，以 **`y=ax²+bx+c`**为例："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [],
   "source": [
    "def quadratic_func(a, b, c):\n",
    "    \"\"\"y=ax²+bx+c\"\"\"\n",
    "\n",
    "    def get_value(x):\n",
    "        return a * x * x + b * x + c\n",
    "\n",
    "    return get_value"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {
    "scrolled": true
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1\n",
      "2\n",
      "5\n",
      "10\n",
      "17\n",
      "26\n"
     ]
    }
   ],
   "source": [
    "# 来个简单的：x^2+1\n",
    "f1 = quadratic_func(1, 0, 1)\n",
    "\n",
    "print(f1(0))\n",
    "print(f1(1))\n",
    "print(f1(2))\n",
    "print(f1(3))\n",
    "print(f1(4))\n",
    "print(f1(5))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 可能不太形象，我们画个图看看：\n",
    "\n",
    "import matplotlib.pyplot as plt # 导入matplotlib的pyplot模块"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[-10, -9, -8, -7, -6, -5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]\n",
      "[101, 82, 65, 50, 37, 26, 17, 10, 5, 2, 1, 2, 5, 10, 17, 26, 37, 50, 65, 82, 101]\n"
     ]
    },
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "# 生成x和y的值\n",
    "x_list = list(range(-10, 11))\n",
    "y_list = [x * x + 1 for x in x_list]\n",
    "\n",
    "print(x_list)\n",
    "print(y_list)\n",
    "\n",
    "# 画图\n",
    "plt.plot(x_list, y_list)\n",
    "# 显示图片\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "-1\n",
      "0\n",
      "3\n",
      "8\n",
      "15\n",
      "24\n"
     ]
    }
   ],
   "source": [
    "# 再来个简单的：x^2-1\n",
    "f2 = quadratic_func(1, 0, -1) # 相互之间不干扰\n",
    "\n",
    "print(f2(0))\n",
    "print(f2(1))\n",
    "print(f2(2))\n",
    "print(f2(3))\n",
    "print(f2(4))\n",
    "print(f2(5))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 8.2.CSharp闭包\n",
    "\n",
    "听完闭包老师就下课了，说什么明天接着闭包讲啥装饰器的。\n",
    "\n",
    "小明一愣一愣的，然后就屁颠的跑黑板前讲起了C#版本的闭包：\n",
    "\n",
    "先看看怎么定义一个闭包，和Python一样，用个求和函数举例：（**返回一个匿名函数**）\n",
    "\n",
    "```csharp\n",
    "// 有返回值就用Func，没有就用Action\n",
    "public static Func<int> SlowSum(params int[] args)\n",
    "{\n",
    "    return () =>\n",
    "    {\n",
    "        int sum = 0;\n",
    "        foreach (var item in args)\n",
    "        {\n",
    "            sum += item;\n",
    "        }\n",
    "        return sum;\n",
    "    };\n",
    "}\n",
    "```\n",
    "调用：\n",
    "```csharp\n",
    "static void Main(string[] args)\n",
    "{\n",
    "    var f1 = SlowSum(1, 2, 3, 4, 5);\n",
    "    Console.WriteLine(f1);\n",
    "    Console.WriteLine(f1());\n",
    "}\n",
    "```\n",
    "结果：(从结果可以看到，f1是一个函数，等你调用f1()才会求和)\n",
    "```\n",
    "System.Func`1[System.Int32]\n",
    "15\n",
    "```\n",
    "\n",
    "---\n",
    "\n",
    "接着讲 ～ 以上面的 **`y=ax²+bx+c`**为例，C#实现：\n",
    "```csharp\n",
    "// 以上面的 y=ax²+bx+c 为例，C#实现：\n",
    "public static Func<double, double> QuadraticFunc(double a, double b, double c)\n",
    "{\n",
    "    return x => a * x * x + b * x + c; // 返回一个匿名函数\n",
    "}\n",
    "```\n",
    "调用：\n",
    "```csharp\n",
    "static void Main(string[] args)\n",
    "{\n",
    "    var func = QuadraticFunc(1, 0, 1);\n",
    "\n",
    "    Console.WriteLine(func(0));\n",
    "    Console.WriteLine(func(1));\n",
    "    Console.WriteLine(func(2));\n",
    "    Console.WriteLine(func(3));\n",
    "    Console.WriteLine(func(4));\n",
    "    Console.WriteLine(func(5));\n",
    "}\n",
    "```\n",
    "结果：\n",
    "```\n",
    "1\n",
    "2\n",
    "5\n",
    "10\n",
    "17\n",
    "26\n",
    "```\n",
    "\n",
    "`Func<double,double>`不理解就看看定义就懂了：`public delegate TResult Func<in T, out TResult>(T arg);`\n",
    "\n",
    "这部分不是很难，简单提一下知识点即可。如果你想深究可以==> **(<a href=\"https://www.cnblogs.com/HQFZ/p/4903400.html\" target=\"_blank\">点</a> <a href=\"https://www.cnblogs.com/happyframework/p/3421754.html\" target=\"_blank\">点</a> <a href=\"https://kb.cnblogs.com/page/111231\" target=\"_blank\">点</a>)**\n",
    "\n",
    "在收获满满一箩筐眼球后，小明拍拍屁股去了新开的饭店大吃一顿了...\n",
    "\n",
    "---\n",
    "\n",
    "写在最后：还有一些内容没写，估计过几天又有一篇叫 **“基础拓展”** 的文章了，为啥不一起写完呢？\n",
    "\n",
    "其实逆天也想写完，真写完文章又被叫做长篇大论一百页了 #^_^# 行了，听取大家意见，不写那么长的文章，下次见～"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.1"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
